#ifdef __linux__
#include "rpc/net/module/epoll.h"

namespace rpc {
  Epoll::~Epoll() {
    if (ep_fd_ != -1) {
      ::close(ep_fd_);
      ep_fd_ = -1;
    }
  }

  bool Epoll::Init() {
    CHECK_EQ(ep_fd_, -1);

    ep_fd_ = ::epoll_create1(EPOLL_CLOEXEC);
    if (ep_fd_ == -1) {
      PLOG(WARNING)<< "epoll_create1 error";
      return false;
    }

    struct epoll_event ev = {0};
    events_.resize(KMaxEvent, ev);
    return true;
  }

  bool Epoll::Add(Event* ev, int flag) {
    if (ev == NULL || flag < 0) return false;
    CHECK_EQ(fds_.count(ev->fd), 0);

    struct epoll_event event = {0};
    event.data.ptr = (void *) ev;
    if (flag & EV_READ) {
      event.events = EPOLLIN;
    }
    if (flag & EV_WRITE) {
      event.events |= EPOLLOUT;
    }
    ev->event = flag;

    int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_ADD, ev->fd, &event);
    if (ret == -1) {
      PLOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
      return false;
    }
    fds_.insert(ev->fd);

    return true;
  }

  void Epoll::Del(Event* ev, int flag) {
    if (ev == NULL || flag <= 0) return;
    CHECK_EQ(fds_.count(ev->fd), 1);

    struct epoll_event event = {0};
    event.data.ptr = (void*) ev;
    if (flag & EV_READ) {
      event.events |= EPOLLIN;
    }
    if (flag & EV_WRITE) {
      event.events |= EPOLLOUT;
    }
    ev->event = flag;

    int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_DEL, ev->fd, &event);
    if (ret == -1) {
      LOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
      return;
    }

    fds_.erase(ev->fd);
  }

  bool Epoll::Mod(Event*ev, int flag) {
    if (ev == NULL || flag <= 0) return false;
    CHECK_EQ(fds_.count(ev->fd), 1);

    struct epoll_event event = {0};
    event.data.ptr = (void*) ev;
    if (flag & EV_READ) {
      event.events |= EPOLLIN;
    }
    if (flag & EV_WRITE) {
      event.events |= EPOLLOUT;
    }
    ev->event = flag;

    int ret = ::epoll_ctl(ep_fd_, EPOLL_CTL_MOD, ev->fd, &event);
    if (ret == -1) {
      LOG(WARNING)<< "epoll_ctl error, fd: " << ev->fd;
      return false;
    }

    return true;
  }

  void Epoll::Loop() {
    loop_tid_ = ::pthread_self();
    exit_ = false;
    while (!exit_) {
      if (fds_.empty()) {
        ::sleep(1);
        continue;
      }

      struct epoll_event ev = {0};
      events_.resize(KMaxEvent, ev);
      int trigger = ::epoll_wait(ep_fd_, events_.data(), events_.size(),
          1000);
      if (trigger == -1) {
        if (errno != EINTR) {
          PLOG(WARNING)<< "epoll_wait error";
        }
        continue;
      }

      for (int i = 0; i < trigger; ++i) {
        struct epoll_event* event = &events_[i];
        Event* ev = (Event*) event->data.ptr;

        short flag = 0;
        if (events_[i].events & EPOLLIN) {
          flag |= EV_READ;
        }
        if (events_[i].events & EPOLLOUT) {
          flag |= EV_WRITE;
        }
        if (events_[i].events & EPOLLERR) {
          flag |= EV_ERROR;
        }

        ev->cb(ev->fd, ev->arg, flag);
      }
      HandleCb();
    }
  }
}

#endif
